package com.woniuxy.controller;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.woniuxy.domain.Order;
import com.woniuxy.domain.Product;
import com.woniuxy.service.IOrderService;
import com.woniuxy.service.IProductService;
import com.woniuxy.service.impl.OrderServiceImpl2;
import com.woniuxy.util.Constants;
import com.woniuxy.util.ResultVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import java.util.Random;

@RestController
//配置中心动态刷新无效时启用
@RefreshScope
@Slf4j
public class OrderController {

    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private IOrderService iOrderService;
    @Autowired
    private DiscoveryClient discoveryClient;
    @Autowired
    private IProductService iProductService;

    @Autowired
    private OrderServiceImpl2 orderServiceImpl2;


    @PostMapping("/orders/{id}/{num}")
    public ResultVO createOrder(@PathVariable Integer id, @PathVariable Integer num) {
        //订单模块调用产品模块,获取产品信息
        //1.原始方式
//        ResultVO resultVO = restTemplate.getForObject("http://localhost:8081/product/" + id, ResultVO.class);

        //2.指定方式
//        List<ServiceInstance> instances = discoveryClient.getInstances("product-service");
//        URI uri = instances.get(0).getUri();
//        ResultVO resultVO = restTemplate.getForObject(uri+"/products/"+id+"/"+num, ResultVO.class);

        //3.负载均衡方式
//        ResultVO resultVO = restTemplate.getForObject("http://product-service/products/" + id + "/" + num, ResultVO.class);

        //4.Feign方式
        ResultVO resultVO = iProductService.findById(id);
        Integer status = resultVO.getStatus();
//因为模块调用返回的数据已经丢失Product格式，实际返回数据为Map,所以需要把map还原为product
        if (status == 200) {
            Product product = new Product();
            try {
                BeanUtils.copyProperties(product, resultVO.getData());
            } catch (Exception e) {
                e.printStackTrace();
            }
            if (num <= product.getStock()) {
                Order order = new Order();
                order.setNumber(num);
                order.setPname(product.getPname());
                order.setPrice(product.getPrice());
                product.setStock(product.getStock() - num);
                order.setPid(id);
                order.setUid(1001);
                order.setUsername("张三");
                //扣减库存
                iProductService.reduceProductStock(product);
                //创建订单
                iOrderService.createOrder(order);
                return new ResultVO(Constants.OPER_SUCCESS, "订单创建成功", order);
            } else {
                return new ResultVO(Constants.OPER_FAILURE, "库存不足，订单创建失败");
            }
        } else {
            return new ResultVO(Constants.OPER_FAILURE, "没有匹配商品，订单创建失败");
        }

    }

    int index = 0;

    @GetMapping("/orders/testloadbalance")
    public ResultVO testLoadBalance() {
//        List<ServiceInstance> instances = discoveryClient.getInstances("product-service");
//        URI uri = instances.get(index++).getUri();
//        if (index>instances.size()-1) {
//            index=0;
//        }
//        return restTemplate.getForObject(uri + "/products/testloadbalance", ResultVO.class);

        return restTemplate.getForObject("http://product-service/products/testloadbalance", ResultVO.class);

    }

    @GetMapping("/orders/message")
    //后备类和后备方法同时存在时调用后备类
    @SentinelResource(blockHandlerClass = OrderControllerBlockHandler.class, blockHandler = "messageBlockHandler", fallbackClass = OrderControllerFallback.class, fallback = "messageFallback")
    public String message() {

        int random = new Random().nextInt(3);
        if (random == 0) {
            log.error("抛异常了");
            throw new RuntimeException();
        }
//        orderServiceImpl2.message();
//        try {
//            Thread.sleep(10);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
        return "message: " + System.currentTimeMillis();
    }

    // 作为后备方法，方法签名必须和原方法一致
// 此外，作为blockHandler还可以在参数列表的最末尾接收一个BlockException类型的参数
// 这个BlockException类型的参数就是用来接收Sentinel监管原方法时，由于限流、降级所抛出的异常
    public String messageBlockHandler(BlockException e) {
        log.error("抛出了BlockException：" + e);
        return "message的后备方法messageBlockHandler";
    }

    // 作为后备方法，方法签名必须和原方法一致
// 此外，作为fallback还可以在参数列表的最末尾接收一个Throwable类型的参数
// 这个Throwable类型的参数可以接收任何类型的异常，包括Sentinel抛出的，以及业务层抛出的业务异常。
    public String messageFallback(Throwable t) {
        return "message的后备方法messageFallback";
    }


    @GetMapping("/orders/message2")
    public String message2() {
        orderServiceImpl2.message();
        return "message2: " + System.currentTimeMillis();
    }

    @GetMapping("/orders/message3")
    @SentinelResource("redian")//热点规则测试必须加上该注解，否则无法生效
    public String message3(String name, Integer age) {
//        int random = new Random().nextInt(3);
//        if (random == 0) {
//            throw new RuntimeException();
//        }
        System.out.println(name);
        System.out.println(age);
        return "message3: " + System.currentTimeMillis();
    }


    @PostMapping("/orders")
    public ResultVO add(@RequestBody Product product) {
        ResultVO resultVO = iProductService.addProduct(product);
//        Product p = new Product();
//        try {
//            BeanUtils.copyProperties(p,resultVO.getData());
//        } catch (IllegalAccessException e) {
//            e.printStackTrace();
//        } catch (InvocationTargetException e) {
//            e.printStackTrace();
//        }
        return new ResultVO(Constants.OPER_SUCCESS, "新增成功");
    }

    @Autowired
    private ConfigurableApplicationContext configurableApplicationContext;

    @GetMapping("testConfig")
    public String testConfig() {
        return  configurableApplicationContext.getEnvironment().getProperty("config.test");
    }

    @Value("${config.test}")
    private String  textTest;

    @GetMapping("testConfig2")
    public String testConfig2() {
        return textTest;
    }
}
